package net.courier.remoting.client;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.pool.BasePoolableObjectFactory;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;

public class ClientPoolFactory {
	private Map<String, GenericObjectPool> poolCache = new ConcurrentHashMap<String, GenericObjectPool>(5);

	private ClientFactory clientFactory;

	public GenericObjectPool generateClientPool(String host, int port) {
		String address = host + ":" + port;
		GenericObjectPool clientPool = poolCache.get(address);
		if (clientPool == null) {
			clientPool = new GenericObjectPool(new PoolableClientFactory(host, port, clientFactory), maxActive,
					whenExhaustedAction, maxWait, maxIdle, minIdle, testOnBorrow, testOnReturn,
					timeBetweenEvictionRunsMillis, numTestsPerEvictionRun, minEvictableIdleTimeMillis, testWhileIdle,
					softMinEvictableIdleTimeMillis, lifo);
			poolCache.put(address, clientPool);
		}

		return clientPool;
	}

	public void setClientFactory(ClientFactory clientFactory) {
		this.clientFactory = clientFactory;
	}

	/**
	 * @see GenericObjectPool#setMaxIdle
	 */
	private int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;
	/**
	 * @see GenericObjectPool#setMinIdle
	 */
	private int minIdle = GenericObjectPool.DEFAULT_MIN_IDLE;
	/**
	 * @see GenericObjectPool#setMaxActive
	 */
	private int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;
	/**
	 * @see GenericObjectPool#setMaxWait
	 */
	private long maxWait = GenericObjectPool.DEFAULT_MAX_WAIT;
	/**
	 * @see GenericObjectPool#setWhenExhaustedAction
	 */
	private byte whenExhaustedAction = GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;
	/**
	 * @see GenericObjectPool#setTestOnBorrow
	 */
	private boolean testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW;
	/**
	 * @see GenericObjectPool#setTestOnReturn
	 */
	private boolean testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN;
	/**
	 * @see GenericObjectPool#setTestWhileIdle
	 */
	private boolean testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE;
	/**
	 * @see GenericObjectPool#setTimeBetweenEvictionRunsMillis
	 */
	private long timeBetweenEvictionRunsMillis = GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
	/**
	 * @see GenericObjectPool#setNumTestsPerEvictionRun
	 */
	private int numTestsPerEvictionRun = GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;
	/**
	 * @see GenericObjectPool#setMinEvictableIdleTimeMillis
	 */
	private long minEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
	/**
	 * @see GenericObjectPool#setSoftMinEvictableIdleTimeMillis
	 */
	private long softMinEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
	/**
	 * @see GenericObjectPool#setLifo
	 */
	private boolean lifo = GenericObjectPool.DEFAULT_LIFO;

	public void setMaxIdle(int maxIdle) {
		this.maxIdle = maxIdle;
	}

	public void setMinIdle(int minIdle) {
		this.minIdle = minIdle;
	}

	public void setMaxActive(int maxActive) {
		this.maxActive = maxActive;
	}

	public void setMaxWait(long maxWait) {
		this.maxWait = maxWait;
	}

	public void setWhenExhaustedAction(byte whenExhaustedAction) {
		this.whenExhaustedAction = whenExhaustedAction;
	}

	public void setTestOnBorrow(boolean testOnBorrow) {
		this.testOnBorrow = testOnBorrow;
	}

	public void setTestOnReturn(boolean testOnReturn) {
		this.testOnReturn = testOnReturn;
	}

	public void setTestWhileIdle(boolean testWhileIdle) {
		this.testWhileIdle = testWhileIdle;
	}

	public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
		this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
	}

	public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
		this.numTestsPerEvictionRun = numTestsPerEvictionRun;
	}

	public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
		this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
	}

	public void setSoftMinEvictableIdleTimeMillis(long softMinEvictableIdleTimeMillis) {
		this.softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
	}

	public void setLifo(boolean lifo) {
		this.lifo = lifo;
	}

	private static class PoolableClientFactory extends BasePoolableObjectFactory implements PoolableObjectFactory {
		private final String host;
		private final int port;
		private final ClientFactory clientFactory;

		public PoolableClientFactory(String host, int port, ClientFactory clientFactory) {
			this.host = host;
			this.port = port;
			this.clientFactory = clientFactory;
		}

		@Override
		public Object makeObject() throws Exception {
			return clientFactory.createClient(host, port);
		}

		@Override
		public void destroyObject(Object obj) throws Exception {
			clientFactory.closeClient((RequestSendClient) obj);
		}
		
		@Override
		public boolean validateObject(Object obj) {
			return ((RequestSendClient) obj).isActive();
		}
	}
}
